#ifndef GAME_ANIMATION_CELL_GENERATOR_H
#define GAME_ANIMATION_CELL_GENERATOR_H
//
// Game Animation.
// Copyright (C) 2007 Department of Computer Science, University of Copenhagen
//
#include <animation/configuration.h>

#include <OpenTissue/core/geometry/geometry_aabb_fit.h>
#include <OpenTissue/core/geometry/geometry_aabb_union.h>
#include <animation/make_heightmap.h>
#include <animation/make_grass.h>
#include <animation/shader/flowers/make_flowers.h>
#include <animation/shader/tree/make_tree.h>
#include <list>
namespace animation
{

  /**
   * Cell Generator Algorithm.
   * Whenever the virtual grid moves new cells appears and old
   * cells are discarded.
   *
   * The cell generator algorihtm is invoked on the new cells. The
   * cell generator algorithm is responsible for setting up the geometry
   * layers in new cells and fixing the AABBs as well.
   *
   * In reality new cells are old cells that are being recycled, so there
   * is really no need for allocating new memory eventhough the virtual
   * grid moves to unexplored regions.
   *
   * It is the GridManager::place() and GridManager::init() metthods that
   * is repsonsible for invoking the cell generator algorithm on the
   * appropriate grid cells.
   */ 
	template<typename vector3_type>
	struct planting_point{
		vector3_type pos;
		vector3_type nor;
	};
  template<typename types>
  class CellGenerator
  {
  public:

    typedef typename types::cell_type        cell_type;
    typedef typename types::noise_type       noise_type;
    typedef typename types::aabb_type        aabb_type;
    typedef typename types::vector3_type     vector3_type;
    typedef typename types::real_type        real_type;

  protected:

    real_type     m_grass_size;   ///< How big is grass clumps
    real_type     m_height_size;  ///< How much varies the height
    unsigned int  m_I;            ///< How many height map cells should be generated along x-axis.
    unsigned int  m_J;            ///< How many height map cells should be generated along y-axis.

  public:

    unsigned int I() const {  return m_I; }
    unsigned int J() const {  return m_J; }

  public:

    CellGenerator() 
      : m_grass_size(0.2f)
      , m_height_size(0.5f)
      , m_I(10)
      , m_J(10)
    {


    }

  public:
	template<typename geometry_type,typename list_type,typename real_type>
    void generate_planting_positions(list_type &points,geometry_type const & surface,real_type/**/){
		typedef typename geometry_type::vertex_type          vertex_type;
		typedef typename geometry_type::vector3_type         vector3_type;
		
		//--- First determine planting positions of all grass clumps.
		OpenTissue::math::Random<real_type> random(0.0f,1.0f);
		srand(100);
		for(unsigned int i=0;i<surface.indices();i=i+3){
			vertex_type const & g0 = surface.get_vertex(surface.get_index(i));
			vertex_type const & g1 = surface.get_vertex(surface.get_index(i+1));
			vertex_type const & g2 = surface.get_vertex(surface.get_index(i+2));
			//for(unsigned int i=0;i<5;++i)
			for(unsigned int i=0;i<40;++i){			
				real_type a0 = random();
				real_type a1 = random();
				if(a0+a1>1.0){
					a0*=0.5;
					a1*=0.5;
				}
				real_type a2 = 1.0 - a1 - a0;
				planting_point<vector3_type> point;
				point.pos=a0*g0.m_coord + a1*g1.m_coord + a2*g2.m_coord;
				point.nor=unit(a0*g0.m_normal + a1*g1.m_normal + a2*g2.m_normal);
				points.push_back( point);
			}
		}
	}
    void operator()(cell_type & cell)
    {
      noise_type noise;

      while(cell.geometries()<6)
        cell.create_geometry();

      real_type grass_height    = m_grass_size;
      real_type grass_halfwidth = m_grass_size/2.0;

	  real_type tree_height=grass_height*20;
	  real_type tree_halfwidth=grass_halfwidth*20;
 

      real_type lx = cell.owner()->get_cell_lower_x(cell.i());
      real_type ly = cell.owner()->get_cell_lower_y(cell.j());
      real_type ux = cell.owner()->get_cell_upper_x(cell.i());
      real_type uy = cell.owner()->get_cell_upper_y(cell.j());
      vector3_type min_coord( lx,ly, - m_height_size/2 );
      vector3_type max_coord( ux,uy,   m_height_size/2 );

      make_heightmap(m_I,m_J,min_coord,max_coord,noise,cell.get_geometry(0));
      cell.get_geometry(0).cleanup_vertex_object();
      aabb_type aabb0,aabb1,aabb2;
      aabb_fit(cell.get_geometry(0).coord_begin(),cell.get_geometry(0).coord_end(),aabb0);
	  make_heightmap(52,52,min_coord,max_coord,noise,cell.get_geometry(4));
		  cell.get_geometry(4).cleanup_vertex_object();

	  std::list<planting_point<vector3_type> > planting_points;
	  generate_planting_positions(planting_points,cell.get_geometry(0),grass_height);

      make_tree(
		  planting_points,
		  cell.get_geometry(2),
		  tree_halfwidth,
		  tree_height,
		  cell.get_geometry(5));
      cell.get_geometry(2).cleanup_vertex_object();
	  cell.get_geometry(5).cleanup_vertex_object();


      make_grass(planting_points,cell.get_geometry(1),grass_halfwidth,grass_height);
      cell.get_geometry(1).cleanup_vertex_object();

      make_flowers(planting_points,cell.get_geometry(3),0.18f/203.0f*83.0f/2.0f,0.18f);
      cell.get_geometry(3).cleanup_vertex_object();

      aabb_fit(cell.get_geometry(0).coord_begin(),cell.get_geometry(0).coord_end(),aabb0);
      //aabb_fit(cell.get_geometry(1).coord_begin(),cell.get_geometry(1).coord_end(),aabb1);
	  //aabb_fit(cell.get_geometry(2).coord_begin(),cell.get_geometry(2).coord_end(),aabb2);

      aabb0.max()(2) += tree_height;
	  //aabb2.max()(2) += tree_height;
      cell.get_aabb() =aabb0;//aabb_union(aabb0,aabb_union(aabb1,aabb2));
    }

  };

} // namespace animation

// GAME_ANIMATION_CELL_GENERATOR_H
#endif
